﻿#pragma once
#ifndef FFMPEG_RECORDING_H
    #define FFMPEG_RECORDING_H

extern "C" {
    #include < WTypesbase.h>

    // ffmpeg 头文件
    #include <libavformat/avformat.h>
    #include <libavdevice/avdevice.h>
    #include <libavcodec/avcodec.h>
    #include <libavutil/opt.h>
    #include <libavutil/channel_layout.h>
    #include <libavutil/samplefmt.h>
    #include <libswresample/swresample.h>
    #include <libavutil/avstring.h>
}

// C++ 库
#include <string>
#include <thread>
#include <atomic>
#include <vector>
#include <map>
#include <queue>
#include <mutex>
#include <condition_variable>

// Qt 库
#include <QObject>

/*
这是强类型枚举的说明。
    强类型枚举中的每个枚举值都有自己的作用域，使用强类型枚举可以避免命名冲突。
    使用 MyIODeviceType::Microphone 来引用枚举值。
    枚举可以用于类型安全的代码。
    强类型枚举的一个优点是每个枚举值都有自己的作用域，使用它可以避免命名冲突。
    默认情况下，枚举类（enum class）的底层类型是 int。
    可以通过指定底层类型来改变枚举值的类型，
    例如 enum class MyIODeviceType : uint8_t。
内存对齐：
    通过指定内存对齐，可以自动调整编译器的填充策略。
    通过这种方式，可以自动选择最合适的填充策略来提高速度。
    当枚举类型作为某个类的成员时，
    指定内存对齐也可以提高效率。
    枚举类型的对齐方式还影响了该成员的访问速度。
    使用 alignas 关键字可以指定对齐方式。
    例如 alignas(4) enum class MyIODeviceType type;
*/
// 强类型枚举
enum class MyIODeviceType : int8_t {
    Unknown = -1,         // 未知
    Camera,               // 摄像机
    Microphone,           // 麦克风
    Speaker,              // 扬声器
    MicrophoneAndSpeaker, // 麦克风和扬声器
    TwoFile               // 两个文件
};
//alignas(4) enum class MyIODeviceType typeA;
//extern MyIODeviceType typeA;
/*
typeA 的使用说明：
    1.赋值 --- 将 typeA 赋值为某个类型，例如：
        typeA = MyIODeviceType::Microphone;
    2.获取 --- 通过判断获取 typeA 的值，并与某个预期的值进行比较
        if (typeA == MyIODeviceType::Microphone) {
            // 处理麦克风输入
        }
此外，还可以对 typeA 进行其他数据操作，例如获取其值。
*/

#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L
extern std::wstring str2wstr(const std::string& str);
extern std::string wstr2str(const std::wstring& wstr);
#endif

// 设备获取类
#ifndef DEVICE_MANAGER_H
    #define DEVICE_MANAGER_H

class DeviceManager
{
public:
    explicit DeviceManager();
    ~DeviceManager();

    // 获取指定类型设备名
    static std::string get_default_device(MyIODeviceType input_type);

    // 枚举所有播放设备并输出输出名称
    static void enumerate_audio_render_devices();

    // 获取输入设备命名列表
    const std::vector<std::string> get_input_devices();

    // 获取输出设备命列表
    //const std::vector<std::string> get_output_devices();
    const std::vector<std::map<std::string, std::string>> get_output_device_list() noexcept;

    // 获取实时音频输出设备数量
    static const unsigned int __stdcall get_sound_devices_count();

    // 获取实时音频输出设备信息
    void update_device_list();


private:
    inline void clean_list();
    void enumerateSoundDevices(DWORD deviceIndex, LPCTSTR* deviceDesc, LPCTSTR* deviceName);

    // 设备列表，这里只获取音频与视频设备，做为 FFmpeg 基类
    std::vector<std::string> input_device_list_;
    //std::vector<std::string> output_device_name_list_;
    std::vector<std::map<std::string, std::string>> output_device_list_; // map 是字典，第一个类型是键的类型，第二个类型是值的类型
};
#endif // DEVICE_MANAGER_H


// FFmpeg 录制类
class FFmpegRecording : public QObject, public DeviceManager
    /*, public std::enable_shared_from_this<FFmpegRecording>*/
{
    Q_OBJECT

public:
    FFmpegRecording(QObject* parent = nullptr);
    ~FFmpegRecording();

    // 外部调用接口
    bool start_record(const std::string& output_file,
        int sample_rate = 44100,
        int channels = 2,
        MyIODeviceType input_type = MyIODeviceType::Microphone,
        const std::string& speaker_file = "");
    // 停止录制
    void stop_record();
    // 暂停录制
    void pause_record();
    // 继续录制
    void resume_record();
    // 返回当前录制状态
    bool is_recording() const;
    bool is_paused() const;


signals:
    // 录制进度
    void record_progress(int64_t duration);


private:
    int64_t     start_pts_ = 0;
    std::string output_file_ = ""; // 单文件录制时麦克风输出/扬声器输出文件或者混音输出文件
    std::string output_file_second_ = ""; // 双文件录制时扬声器输出文件
    int         sample_rate_ = 44100;
    int         channels_ = 2;
    bool        recording_ = false;
    bool        paused_ = false; // 是否暂停录制
    MyIODeviceType input_type_ = MyIODeviceType::Microphone;

    // microphone 相关
    AVFormatContext*        microphone_input_ctx_ = nullptr;
    AVFormatContext*        microphone_output_ctx_ = nullptr;
    AVCodecContext*         microphone_encoder_ctx_ = nullptr;
    AVStream*               microphone_out_stream_ = nullptr;
    SwrContext*             microphone_swr_ctx_ = nullptr;
    AVPacket*               microphone_pkt_ = nullptr;
    AVFrame*                microphone_frame_ = nullptr;
    //std::queue<AVFrame*>    microphone_frame_queue_; //网络使用
    std::thread             microphone_queue_thread_;
    std::thread             microphone_save_thread_;
    std::atomic<bool>       microphone_thread1_running_{ false };
    //std::atomic<bool>       microphone_thread2_running_{ false };

    /*
    std::queue<std::vector<uint8_t>> pcm_queue_;
    std::mutex pcm_mutex_;
    std::condition_variable pcm_cv_;
    bool capture_finished_ = false;
    */

    inline void cleanup(MyIODeviceType input_type = MyIODeviceType::MicrophoneAndSpeaker);
    bool record_output_device_aac(const std::string& device_id, const std::string& out_file, int sample_rate, int channels);
    bool record_microphone_aac(int sample_rate, int channels, const AVInputFormat* input_fmt, std::string& device_name);
    bool main_recording(
        int sample_rate = 44100,
        int channels = 2,
        MyIODeviceType input_type = MyIODeviceType::Microphone);
};
#endif // FFMPEG_RECORDING_H

/*
AAC 格式支持的采样率范围为 8 kHz 到 96 kHz 之间。具体的采样率包括：
    8 kHz、11.025 kHz、12 kHz、16 kHz、22.05 kHz、24 kHz、
    32 kHz、44.1 kHz、48 kHz、64 kHz、88.2 kHz、96 kHz

AAC 格式支持的音频格式包括：
    AV_SAMPLE_FMT_FLTP --- 浮点型，32 位，平面格式，每个样本一个通道
    AV_SAMPLE_FMT_FLT --- 浮点型，32 位，交织格式，每个样本交织一个通道
    AV_SAMPLE_FMT_S16 --- 签名整型，16 位，交织格式
    AV_SAMPLE_FMT_S16P --- 签名整型，16 位，平面格式
    AV_SAMPLE_FMT_S32 --- 签名整型，32 位，交织格式
    AV_SAMPLE_FMT_S32P --- 签名整型，32 位，平面格式
    AV_SAMPLE_FMT_U8 --- 无符号整型，8 位，交织格式
    AV_SAMPLE_FMT_U8P --- 无符号整型，8 位，平面格式

AAC 格式支持的声道布局包括：
    AV_CHANNEL_LAYOUT_MONO --- 单声道
    AV_CHANNEL_LAYOUT_STEREO --- 立体声
    AV_CHANNEL_LAYOUT_SURROUND --- 环绕声
    AV_CHANNEL_LAYOUT_4POINT1 --- 4.1 声道
    AV_CHANNEL_LAYOUT_5POINT1 --- 5.1 声道
    AV_CHANNEL_LAYOUT_7POINT1 --- 7.1 声道

通过配置 44.1 kHz、2 声道、AV_SAMPLE_FMT_S16P 和 AV_CHANNEL_LAYOUT_STEREO，可以作为 AAC 格式的默认配置。
*/